#pragma once
#include "DataStream.h"
#include <vector>

//-----------------------------------------------------------------------
// Serializer
//-----------------------------------------------------------------------
class Serializer
{
protected:
	bool mSaving;
	DataStream* mDataStream;

public:
	Serializer(bool saving) 
		: mSaving(saving), mDataStream(NULL)
	{}
	virtual ~Serializer() 
	{
		if (mDataStream) 
			delete mDataStream; 
	}

	bool IsSaving() const { return mSaving; }
	bool IsLoading() const { return !mSaving; }
	bool Skip(int off) { return mDataStream->Skip(off); }
	bool Good() const { return mDataStream->Good(); }
	bool Eof() const { return mDataStream->Eof(); }

	// init for serializing...
	bool Initialize(const char* fileName)
	{
		mDataStream = new FileDataStream(fileName, IsSaving());
		if (!mDataStream->Good())
			return false;
		return true;
	}

	bool Initialize(const void* buff, size_t size)
	{
		mDataStream = new MemeoryDataStream(buff, size);
		return true;
	}


	// main entry.
	size_t Serialize(void* v, int Length)
	{
		assert(mDataStream && "You should initialize serializer.");

		if (IsSaving())
			return mDataStream->Write(static_cast<const char*>(v), Length);
		else
			return mDataStream->Read(static_cast<char*>(v), Length);
	}

	size_t Serialize(const void* v, int Length)
	{
		assert (IsSaving() && "Can't write to variable with loading.");
		return mDataStream->Write(v, Length);
	}
	
	// general serialize.
	template <class T>
	friend Serializer& operator<<(Serializer& ser, T& v)
	{
		ser.Serialize(&v, sizeof(v));
		return ser;
	}
	
	template <class T>
	friend Serializer& operator<<(Serializer& ser, const T& v)
	{
		ser.Serialize(&v, sizeof(v));
		return ser;
	}

	friend Serializer& operator<<(Serializer& ser, std::string& v)
	{
		if (ser.IsLoading())
		{
			const int STREAM_SIZE = 128;
			char stream[STREAM_SIZE];
			v.reserve(STREAM_SIZE);

			size_t readed = 0;
			while ((readed = ser.Serialize(stream, STREAM_SIZE)) > 0)
			{
				for (size_t i = 0; i < readed; i++)
				{
					if (stream[i] == '\n' || stream[i] == 0)
					{
						ser.Skip(-STREAM_SIZE + i + 1);
						return ser;
					}
					v.push_back(stream[i]);
				}
			}
		}
		else
		{
			ser.Serialize(&v[0], v.size());
			ser << '\n';
		}

		return ser;
	}
	
	template <class T>
	friend Serializer& BlockSerialize(Serializer& ser, std::vector<T>& v)
	{
		// get the size.
		size_t size = v.size();
		ser << size;
		
		// resize array, if size is the same, donothing.
		v.resize(size);

		if (size != 0)
			ser.Serialize(&v[0], static_cast<int>(sizeof(T) * size));
		return ser;
	}

	// vector serialize.
	template <class T>
	friend Serializer& operator<<(Serializer& ser, std::vector<T>& v)
	{
		// get the size.
		size_t size = v.size();
		ser << size;
		
		// resize array, if size is the same, donothing.
		v.resize(size);
		
		for (std::vector<T>::iterator it(v.begin()), end(v.end()); it != end; it++)
			ser << *it;
		return ser;
	}

	friend Serializer& operator<<(Serializer& ser, std::vector<char>& v) { return BlockSerialize(ser, v); }
	friend Serializer& operator<<(Serializer& ser, std::vector<short>& v) { return BlockSerialize(ser, v); }
	friend Serializer& operator<<(Serializer& ser, std::vector<int>& v) { return BlockSerialize(ser, v); }

	friend Serializer& operator<<(Serializer& ser, std::vector<unsigned char>& v) { return BlockSerialize(ser, v); }
	friend Serializer& operator<<(Serializer& ser, std::vector<unsigned short>& v) { return BlockSerialize(ser, v); }
	friend Serializer& operator<<(Serializer& ser, std::vector<unsigned int>& v) { return BlockSerialize(ser, v); }
	
	friend Serializer& operator<<(Serializer& ser, std::vector<float>& v) { return BlockSerialize(ser, v); }
	friend Serializer& operator<<(Serializer& ser, std::vector<double>& v) { return BlockSerialize(ser, v); }
};
